package com.huantai.common.util.excel;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;

import com.huantai.common.enums.ExcelVersionEnum;
import com.huantai.common.enums.ImgFilePathTypeEnum;
import com.huantai.common.exception.BusinessException;
import com.huantai.common.exception.SystemException;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.huantai.common.util.StringUtil;

/**
 * POI处理Excel的工具类，仅支持[Microsoft Office Excel 2007]版本及以上
 * 
 * @author Ma Chao
 * @version 2018年4月17日 下午6:00:51
 */
public final class PoiUtil {
    /** 最大行数 */
    static final int MAX_ROW_COUNT = 1048576;
    /** 最大列数 */
    static final int MAX_COLUMN_COUNT = 16384;

    private PoiUtil() {}

    /**
     * 验证是不是Excel文件
     * 
     * @param fileName 文件名
     * @return boolean
     */
    public static boolean isExcelFile(String fileName) {
        if (StringUtil.isEmpty(fileName)) {
            return false;
        }
        return fileName.endsWith(ExcelVersionEnum.EXCEL_2003.getSuffix())
                || fileName.endsWith(ExcelVersionEnum.EXCEL_2007.getSuffix());
    }

    /**
     * 根据文件名获得Excel版本
     *
     * @param fileName 文件名
     * @return ExcelVersionEnum
     */
    public static ExcelVersionEnum getExcelVersion(String fileName) {
        if (StringUtil.isEmpty(fileName)) {
            return null;
        }
        int indexOfLastPoint = fileName.lastIndexOf('.');
        String suffix = fileName.substring(indexOfLastPoint);
        return ExcelVersionEnum.getBySuffix(suffix);
    }

    /**
     * 获取模版文件
     * 
     * @Title: getTemplateFile
     * @param filePath 文件路径
     * @return Workbook
     */
    public static Workbook getTemplateFile(String filePath) {
        if (!isExcelFile(filePath)) {
            throw new BusinessException(
                    StringUtil.formatMessage("输入文件{0}不是Microsoft Office Excel文件！", filePath));
        }
        ExcelVersionEnum excelVersion = getExcelVersion(filePath);
        if (ExcelVersionEnum.EXCEL_2007 != excelVersion) {
            throw new BusinessException(StringUtil.formatMessage("不支持{0}文件类型！",
                    ExcelVersionEnum.EXCEL_2003.getSuffix()));
        }
        Workbook workbook = null;
        try {
            workbook = new XSSFWorkbook(filePath);
        } catch (IOException e) {
            throw new BusinessException(StringUtil.formatMessage("获取文件{0}失败！", filePath), e);
        }
        return workbook;
    }

    /**
     * 
     * 设置单元格样式
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @param cs 单元格样式
     */
    public static void setCellStyle(Sheet sheet, int x, int y, CellStyle cs) {
        Cell cell = getOrCreateCell(sheet, x, y);
        if (cell != null) {
            sheet.getRow(x - 1).getCell(y - 1).setCellStyle(cs);
        }
    }

    /**
     * 单元格赋值
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @param strValue String值
     * @return 赋值完毕的单元格
     */
    public static Cell setCellValue(Sheet sheet, int x, int y, String strValue) {
        Cell cell = getOrCreateCell(sheet, x, y);
        if (cell != null && strValue != null) {
            if (sheet instanceof HSSFSheet) {
                RichTextString fts = new HSSFRichTextString(strValue);
                cell.setCellValue(fts);
            } else {
                RichTextString fts = new XSSFRichTextString(strValue);
                cell.setCellValue(fts);
            }
        }
        return cell;
    }

    /**
     * 单元格赋值
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @param blValue 布尔值
     * @return 赋值完毕的单元格
     */
    public static Cell setCellValue(Sheet sheet, int x, int y, boolean blValue) {
        Cell cell = getOrCreateCell(sheet, x, y);
        if (cell != null) {
            cell.setCellValue(blValue);
        }
        return cell;
    }

    /**
     * 单元格赋值
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @param nValue 数值
     * @return 赋值完毕的单元格
     */
    public static Cell setCellValue(Sheet sheet, int x, int y, int nValue) {
        Cell cell = getOrCreateCell(sheet, x, y);
        if (cell != null) {
            cell.setCellValue(nValue);
        }
        return cell;
    }

    /**
     * 单元格赋值
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @param nValue 数值
     * @return 赋值完毕的单元格
     */
    public static Cell setCellValue(Sheet sheet, int x, int y, Integer nValue) {
        Cell cell = getOrCreateCell(sheet, x, y);
        if (cell != null && nValue != null) {
            cell.setCellValue(nValue);
        }
        return cell;
    }

    /**
     * 单元格赋值
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @param dValue 数值
     * @return 赋值完毕的单元格
     */
    public static Cell setCellValue(Sheet sheet, int x, int y, double dValue) {
        Cell cell = getOrCreateCell(sheet, x, y);
        if (cell != null) {
            cell.setCellValue(dValue);
        }
        return cell;
    }

    /**
     * 单元格赋值
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @param dValue 时间日期值
     * @return 赋值完毕的单元格
     */
    public static Cell setCellValue(Sheet sheet, int x, int y, Date dValue) {
        Cell cell = getOrCreateCell(sheet, x, y);
        if (cell != null && dValue != null) {
            cell.setCellValue(dValue);
        }
        return cell;
    }

    /**
     * 单元格赋值
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @param bValue 时间日期值
     * @return 赋值完毕的单元格
     */
    public static Cell setCellValue(Sheet sheet, int x, int y, BigDecimal bValue) {
        Cell cell = getOrCreateCell(sheet, x, y);
        if (cell != null && bValue != null) {
            cell.setCellValue(bValue.doubleValue());
        }
        return cell;
    }

    /**
     * 设置单元格公式
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @param strFormula 公式
     * @return 设置完公式的单元格
     */
    public static Cell setCellFormula(Sheet sheet, int x, int y, String strFormula) {
        Cell cell = getOrCreateCell(sheet, x, y);
        if (cell != null && strFormula != null) {
            cell.setCellFormula(strFormula);
        }
        return cell;
    }

    /**
     * 获取或创建单元格
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @return 对应的单元格
     */
    public static Cell getOrCreateCell(Sheet sheet, int x, int y) {
        if (sheet instanceof HSSFSheet) {
            throw new BusinessException(StringUtil.formatMessage("不支持{0}文件类型！",
                    ExcelVersionEnum.EXCEL_2003.getSuffix()));
        } else {
            if (x < 1 || x > MAX_ROW_COUNT || y < 1 || y > MAX_COLUMN_COUNT) {
                throw new BusinessException(
                        StringUtil.formatMessage("单元格行号{0}超出范围{1},{2}或列号{3}超出范围{4},{5}！", x, 1,
                                MAX_ROW_COUNT, y, 1, MAX_COLUMN_COUNT));
            }
        }

        Row row = sheet.getRow(x - 1);
        if (null == row) {
            row = sheet.createRow(x - 1);
        }
        Cell cell = row.getCell(y - 1);
        if (null == cell) {
            cell = row.createCell(y - 1);
        }
        return cell;
    }

    /**
     * 合并单元格
     * 
     * @param sheet 工作表
     * @param beginRow 开始行（第1行从1开始计数）
     * @param endRow 结束行（第1行从1开始计数）
     * @param beginColumn 开始列（第1列从1开始计数）
     * @param endColumn 结束列（第1列从1开始计数）
     */
    public static void mergedRegion(Sheet sheet, int beginRow, int endRow, int beginColumn,
            int endColumn) {
        CellRangeAddress reangeAddr =
                new CellRangeAddress(beginRow - 1, endRow - 1, beginColumn - 1, endColumn - 1);
        sheet.addMergedRegion(reangeAddr);
    }

    /**
     * 插入图片
     * 
     * @param workbook 目标工作簿
     * @param sheet 工作表
     * @param imageFilePath 图片文件的路径
     * @param pathType 图片路径类型
     * @param beginRow 开始行（第1行从1开始计数）
     * @param endRow 结束行（第1行从1开始计数）
     * @param beginColumn 开始列（第1列从1开始计数）
     * @param endColumn 结束列（第1列从1开始计数）
     */
    @SuppressWarnings("rawtypes")
    public static void setImage(Workbook workbook, Sheet sheet, String imageFilePath, // NOSONAR
                                ImgFilePathTypeEnum pathType, int beginRow, int endRow, int beginColumn,
                                int endColumn) {
        InputStream is = null;
        HttpURLConnection httpUrlConn = null;
        try {
            // 根据图片路径类型获取图片文件信息
            if (ImgFilePathTypeEnum.LOCAL == pathType) {
                is = new FileInputStream(imageFilePath);
            } else {
                URL url = new URL(imageFilePath);
                httpUrlConn = (HttpURLConnection) url.openConnection();
                httpUrlConn.connect();
                is = new BufferedInputStream(httpUrlConn.getInputStream());
            }
            // 将图片文件流入工作簿
            byte[] bytes = IOUtils.toByteArray(is);
            int pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
            is.close();
            if (null != httpUrlConn) {
                httpUrlConn.disconnect();
            }

            // 绘制图片到目标工作表
            CreationHelper helper = workbook.getCreationHelper();
            Drawing drawing = sheet.createDrawingPatriarch();
            ClientAnchor anchor = helper.createClientAnchor();
            anchor.setRow1(beginRow - 1);
            anchor.setCol1(beginColumn - 1);
            anchor.setRow2(endRow);
            anchor.setCol2(endColumn);
            anchor.setDx1(100);
            anchor.setDy1(100);
            anchor.setDx2(-100);
            anchor.setDy2(-100);
            drawing.createPicture(anchor, pictureIdx);
        } catch (IOException e) {
            throw new SystemException(StringUtil.formatMessage("获取文件{0}失败！", imageFilePath), e);
        }
    }

    /**
     * 数据有效性选择
     * 
     * @param sheet 工作表
     * @param beginRow 开始行（第1行从1开始计数）
     * @param endRow 结束行（第1行从1开始计数）
     * @param beginColumn 开始列（第1列从1开始计数）
     * @param endColumn 结束列（第1列从1开始计数）
     * @param explicitListValues 可用数据集合
     * @throws BusinessException
     */
    public static void setValidationData(Sheet sheet, int beginRow, int endRow, int beginColumn,
            int endColumn, String[] explicitListValues) {
        if (beginRow < 0 || endRow < 0 || beginColumn < 0 || endColumn < 0 || endRow < beginRow
                || endColumn < beginColumn) {
            throw new BusinessException(
                    StringUtil.formatMessage("输入参数有错：开始行{0}超出范围{1},{2}或开始列{3}超出范围{4},{5}", beginRow,
                            0, endRow, beginColumn, 0, endColumn));
        }
        DataValidationHelper dvHelper = sheet.getDataValidationHelper();
        DataValidationConstraint dvConstraint =
                dvHelper.createExplicitListConstraint(explicitListValues);
        CellRangeAddressList addressList =
                new CellRangeAddressList(beginRow - 1, endRow - 1, beginColumn - 1, endColumn - 1);
        DataValidation validation = dvHelper.createValidation(dvConstraint, addressList);
        if (validation instanceof XSSFDataValidation) {
            validation.setSuppressDropDownArrow(true);
            validation.setShowErrorBox(true);
        } else {
            validation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(validation);
    }

    /**
     * 获取单元格样式
     * 
     * @param sheet 工作表
     * @param x 行号（第1行从1开始计数）
     * @param y 列号（第1列从1开始计数）
     * @return 单元格样式
     */
    public static CellStyle getCellStyle(Sheet sheet, int x, int y) {
        Row row = sheet.getRow(x - 1);
        Cell cell = row.getCell(y - 1);
        return cell.getCellStyle();
    }

    /**
     * 获取一行单元格样式
     * 
     * @param sheet 工作表
     * @param rowNum 行号（第1行从1开始计数）
     * @param endColumn 结束列（第1列从1开始计数）
     * @return 一行单元格样式
     */
    public static CellStyle[] getCellStylesOfRow(Sheet sheet, int rowNum, int endColumn) {
        CellStyle[] styles = new CellStyle[endColumn];
        Row row = sheet.getRow(rowNum - 1);
        for (int i = 0; i < endColumn; i++) {
            Cell cell = row.getCell(i);
            styles[i] = cell.getCellStyle();
        }
        return styles;
    }

    /**
     * 创建单元格
     * 
     * @param row 目标行
     * @param index index
     * @param value 值
     * @param style 样式
     */
    public static void createCell(Row row, int index, String value, CellStyle style) {
        Cell cell = row.createCell(index);
        cell.setCellStyle(style);
        cell.setCellValue(value);
    }

    /**
     * 创建单元格
     * 
     * @param row 目标行
     * @param index index
     * @param value 值
     * @param style 样式
     */
    public static void createCell(Row row, int index, Integer value, CellStyle style) {
        Cell cell = row.createCell(index);
        cell.setCellStyle(style);
        cell.setCellValue(value);
    }

    /**
     * 创建单元格
     * 
     * @param row 目标行
     * @param index index
     * @param value 值
     * @param style 样式
     */
    public static void createCell(Row row, int index, boolean value, CellStyle style) {
        Cell cell = row.createCell(index);
        cell.setCellStyle(style);
        cell.setCellValue(value);
    }

    /**
     * 创建单元格
     * 
     * @param row 目标行
     * @param index index
     * @param value 值
     * @param style 样式
     */
    public static void createCell(Row row, int index, Date value, CellStyle style) {
        Cell cell = row.createCell(index);
        cell.setCellStyle(style);
        cell.setCellValue(value);
    }

    /**
     * 创建单元格
     * 
     * @param row 目标行
     * @param index index
     * @param value 值
     * @param style 样式
     */
    public static void createCell(Row row, int index, BigDecimal value, CellStyle style) {
        Cell cell = row.createCell(index);
        cell.setCellStyle(style);
        cell.setCellValue(value.doubleValue());
    }

    /**
     * Remove specified sheet from workbook by sheet name.
     * 
     * @param workbook the Workbook object.
     * @param sheetName the name of sheet to remove.
     * @return true is removed success.
     */
    public static boolean removeSheet(Workbook workbook, String sheetName) {
        boolean result = false;
        if (workbook != null && !StringUtil.isEmpty(sheetName)) {
            int index = workbook.getSheetIndex(sheetName);
            if (index >= 0) {
                workbook.removeSheetAt(index);
                result = true;
            }
        }
        return result;
    }
}
